/*
 * Copyright (c) 2021 Zsj. All rights reserved.
 */

package com.jzz.crawlerweb.handler.handler;

import com.jzz.crawlerweb.handler.data.CalculationCaptcha;
import com.jzz.crawlerweb.handler.data.Captcha;

import java.util.Arrays;
import java.util.Random;

public class CalculationCaptchaHandler extends AbstractCaptchaHandler<byte[]> {
    private static final Random random = new Random();
    private static final int[] op = new int[]{/* + */0, /* - */1, /* * */2};

    // 简便起见，使用10以内的加减乘除
    private static final int bound = 10;

    @Override
    public Captcha<byte[]> generate() {
        // 生成一个三元运算式子
        Captcha<byte[]> captcha = new CalculationCaptcha();
        StringBuilder sb = new StringBuilder();
        sb.append(random.nextInt(bound)).append(" ")
                .append(op[random.nextInt(3)]).append(" ")
                .append(random.nextInt(bound)).append(" ")
                .append(op[random.nextInt(3)]).append(" ")
                .append(random.nextInt(bound));

        captcha.setRaw(sb.toString());
        return captcha;
    }

    @Override
    public boolean verify(String input, String stored) {
        return String.valueOf(result(stored)).equals(input);
    }

    /**
     * 计算一个三元表达式的值
     * 表达式为：n1 [op1] n2 [op2] n3
     * 如：12 + 23 * 4
     *
     * @param op1 第一个运算符
     * @param op2 第二个运算符
     * @param n1  第一个操作数
     * @param n2  第二个操作数
     * @param n3  第三个操作数
     * @return
     */
    public int result(int op1, int op2, int n1, int n2, int n3) {
        int ret = 0;

        if (op2 == op[2]) {
            // 若第二个操作符是*，则优先计算，这样就解决了运算优先级问题
            ret = calc(op2, n2, n3);
            ret = calc(op1, n1, ret);
        } else {
            ret = calc(op1, n1, n2);
            ret = calc(op2, ret, n3);
        }
        return ret;
    }

    public int result(String input) {
        if (input.matches("[0-9]+ [0-2] [0-9]+ [0-2] [0-9]+")) {
            int[] splits = Arrays.stream(input.split(" ")).mapToInt(Integer::parseInt).toArray();
            return result(
                    splits[1],
                    splits[3],
                    splits[0],
                    splits[2],
                    splits[4]
            );
        } else {
            return 0;
        }
    }

    /**
     * Calculate a [op] b
     *
     * @param op Operator
     * @param a  Number a
     * @param b  Number b
     * @return result
     */
    private int calc(int op, int a, int b) {
        switch (op) {
            case 0:
                return a + b;
            case 1:
                return a - b;
            case 2:
                return a * b;
            default:
                return 0;
        }
    }
}
